<template>
  <div class="v-table-wrapper">
    <table class="v-table" v-box-select>
      <tr v-for="(row, i) in tables" :key="i">
        <template v-for="(td, j) in row">
          <td
            v-if="td.w != 0 && td.h != 0"
            :key="j"
            :colspan="td.w"
            :rowspan="td.h"
            @dragover.prevent
            @drop="onDrop"
            @click="onCellClick"
          >
            <!-- 单元格遮罩 -->
            <el-popover ref="popover" trigger="contextmenu" placement="left">
              <template #default>
                <VCellControl :arr="tables" v-bind="td" :x="i" :y="j" @update:tables="update">
                  <div v-if="isUse(td.id)" class="v-cell-control-item" @click="remove(td.id)">
                    移除表单项
                  </div>
                </VCellControl>
              </template>
              <template #reference>
                <div
                  class="v-cell"
                  :class="{ active: isActive(td.id), current: currentTdId == td.id }"
                  :x="i"
                  :y="j"
                  :id="td.id"
                ></div>
              </template>
            </el-popover>
            <!-- 单元格内容 -->
            <div class="v-cell-content">
              <slot name="cell" :tdId="td.id"></slot>
            </div>
          </td>
        </template>
      </tr>
    </table>
    <el-alert
      title="合并单元格"
      type="info"
      show-icon
      :closable="false"
      style="width: 800px; margin: 20px"
    >
      1、框选的单元格，释放鼠标提示是否合并单元格，确认合并，高亮的单元格将合并在一起。<br />
      2、如果需要取消合并单元格，请在单元格右侧菜单中选中【解除合并单元格】。
    </el-alert>
  </div>
</template>

<script setup lang="ts">
import { ElMessageBox, ElMessage } from 'element-plus'
import { getRowCells, getColCells, mergeColCell, mergeRowCell } from './utils'
// 表格数据和方法
const { tables, currentTdId, setCurrent, updateTables } = inject<{
  tables: any
  currentTdId: any
  setCurrent: (tdId: string) => void
  updateTables: (arr: any[][]) => void
}>('tables', {
  tables: [],
  currentTdId: '',
  setCurrent: (tdId: string) => {},
  updateTables: () => {}
})

// 表单数据和方法
const { forms, addFormItem, removeFormItem } = inject<{
  forms: any
  addFormItem: (tdId: string, formType: string, formTypeName: string, formTypeIcon: string) => void
  removeFormItem: (tdId: string) => void
}>('forms', {
  forms: [],
  addFormItem: (tdId: string, formType: string, formTypeName: string, formTypeIcon: string) => {},
  removeFormItem: (tdId: string) => {}
})

// 选中单元格
const currents = ref<
  {
    x: number
    y: number
    w: number
    h: number
    id: string
  }[]
>([])

// 单元格是否使用
const isUse = (tdId: string) => {
  return forms.value.find((el: any) => el.tdId === tdId)
}

// 当前选中单元格
const isActive = (id: string) => {
  const currentIds = currents.value.map((item: any) => item.id)
  return currentIds.includes(id) ? true : false
}

// 选中单元格
const addSelectCell = (start: MouseEvent, end: MouseEvent) => {
  currents.value = []
  const startDom = start.target as HTMLElement
  const endDom = end.target as HTMLElement
  if (!startDom || !endDom) return
  const sx = Number(startDom.getAttribute('x'))
  const sy = Number(startDom.getAttribute('y'))
  const ex = Number(endDom.getAttribute('x'))
  const ey = Number(endDom.getAttribute('y'))
  const x = Math.abs(ex - sx)
  const y = Math.abs(ey - sy)
  if (x > 0 && y == 0 && tables.value) {
    if (sx < ex) {
      currents.value = getColCells(tables.value, sx, sy, x + 1)
    }
    if (sx > ex) {
      currents.value = getColCells(tables.value, ex, ey, x + 1)
    }
  }
  if (y > 0 && x == 0 && tables.value) {
    if (sy < ey) {
      currents.value = getRowCells(tables.value, sx, sy, y + 1)
    }
    if (sy > ey) {
      currents.value = getRowCells(tables.value, ex, ey, y + 1)
    }
  }
}

// 校验合并
const verifyMerge = () => {
  const isUseArr = currents.value
    .map((el) => {
      return isUse(el.id)
    })
    .filter((el) => {
      return el
    })
  if (isUseArr.length > 1) {
    ElMessage.error('选中单元格存在多个表单项，合并失败！')
    return false
  }
  return true
}

// 确定合并
const submitMerge = () => {
  const startCell = currents.value[0]
  const n = currents.value.length
  const endCell = currents.value[currents.value.length - 1]
  if (!startCell) return
  if (!verifyMerge()) return
  if (startCell.x === endCell.x && tables.value) {
    // 跨列合并
    mergeColCell(tables.value, startCell.x, startCell.y, n)
  }
  if (startCell.y === endCell.y && tables.value) {
    // 跨行合并
    mergeRowCell(tables.value, startCell.x, startCell.y, n)
  }
  currents.value = []
  update(tables.value)
}

const update = (arr: any[][]) => {
  updateTables(arr)
}

// 目标区域 获取拖拽元素数据
const onDrop = (e: DragEvent) => {
  e.preventDefault()
  if (e.dataTransfer && e.target) {
    const td = e.target as HTMLElement
    const tdId = td.id
    const formType = e.dataTransfer.getData('type')
    const formTypeName = e.dataTransfer.getData('type-name')
    const formTypeIcon = e.dataTransfer.getData('type-icon')
    // 添加表单项
    addFormItem(tdId, formType, formTypeName, formTypeIcon)
  }
}

// 点击单元格
const onCellClick = (e: MouseEvent) => {
  if (e.target) {
    const td = e.target as HTMLElement
    const tdId = td.id
    // 添加表单项
    setCurrent(tdId)
  }
}

// 删除表单项
const remove = (tdId: string) => {
  removeFormItem(tdId)
}

// 框选区域
const vBoxSelect = {
  mounted: (el: HTMLElement) => {
    el.addEventListener('mousedown', (e: MouseEvent) => {
      e.preventDefault() // 阻止默认行为（如文本选择）

      const x = e.clientX // 记录初始点击位置的x坐标
      const y = e.clientY // 记录初始点击位置的y坐标
      let start = e
      // 选中单元格
      addSelectCell(start, e)

      // 框选区域
      let selectionBox: HTMLDivElement | null = document.createElement('div') // 创建选区元素
      selectionBox.id = 'selection-box' // 添加ID属性
      selectionBox.style.left = x + 'px' // 根据起始点和当前点确定左上角的x坐标
      selectionBox.style.top = y + 'px' // 根据起始点和当前点确定左上角的y坐标
      el.appendChild(selectionBox) // 将选区元素添加到容器内

      el.addEventListener('mousemove', moveSelection) // 添加移动时间监听器
      el.addEventListener('mouseup', endSelection) // 添加结束时间监听器

      // 移动时间监听器
      function moveSelection(e: MouseEvent) {
        if (selectionBox !== null) {
          var width = Math.abs(e.clientX - x) // 计算选区的宽度
          var height = Math.abs(e.clientY - y) // 计算选区的高度

          selectionBox.style.left = Math.min(e.clientX, x) + 'px' // 根据起始点和当前点确定左上角的x坐标
          selectionBox.style.top = Math.min(e.clientY, y) + 'px' // 根据起始点和当前点确定左上角的y坐标
          selectionBox.style.width = width + 'px' // 设置选区的宽度
          selectionBox.style.height = height + 'px' // 设置选区的高度
          // 选中单元格
          addSelectCell(start, e)
        }

        e.stopPropagation() // 防止事件传播到父元素或其他处理函数
      }
      // 结束时间监听器
      function endSelection(e: MouseEvent) {
        el.removeEventListener('mousemove', moveSelection) // 移除移动时间监听器
        el.removeEventListener('mouseup', endSelection) // 移除结束时间监听器

        if (selectionBox !== null && selectionBox.parentNode === el) {
          el.removeChild(selectionBox) // 从DOM中删除选区元素
          // 选中单元格
          addSelectCell(start, e)
          // 提示是否合并
          if (currents.value[0]) {
            ElMessageBox.confirm('是否确定合并单元格', '提示', {
              confirmButtonText: '确定',
              cancelButtonText: '取消'
            })
              .then(() => {
                submitMerge()
              })
              .catch(() => {
                currents.value = []
              })
          }
        }

        selectionBox = null // 重置选区变量
      }
    })
  }
}
</script>

<script lang="ts">
export default {
  name: 'VTable'
}
</script>

<style lang="scss">
$border-width: 1px;
$border-style: solid;
$border-color: #ccc;
$text-color: #333;
$theme-color: #ff5607;
.v-table-wrapper {
}
.v-table {
  margin: 0 auto;
  border: $border-width $border-style $border-color;
  border-collapse: collapse; /* 去除边框之间的间隙 */
  border-spacing: 0; /* 去除单元格之间的间隙 */
  td {
    padding: 0;
    border: $border-width $border-style $border-color;
    vertical-align: top;
    position: relative;
    .v-cell {
      position: absolute;
      top: -1px;
      right: -1px;
      bottom: -1px;
      left: -1px;
      z-index: 5;
      &.current {
        border: 1px solid $theme-color;
      }
      &.active {
        background-color: rgba($theme-color, 0.2);
      }
    }
    .v-cell-content {
      min-width: 300px;
      min-height: 50px;
      display: flex;
      align-items: center;
    }
  }
  #selection-box {
    position: fixed;
    border: 2px solid $theme-color;
  }
}
.v-cell-control-list {
  .v-cell-control-title {
    border-bottom: 1px solid #dddddd;
    padding-bottom: 12px;
    margin-bottom: 12px;
  }
  .v-cell-control-item {
    font-size: 16px;
    cursor: pointer;
    padding: 5px 10px;
    &:hover {
      color: $theme-color;
    }
  }
}
</style>
